/*
 * LocalEqualTo.java
 *
 * Created on January 10, 2005, 1:28 AM
 */

package edu.temple.GUS.MMA.Functions;

import java.util.ArrayList;
import edu.temple.GUS.MMA.DataTypes.*;
import edu.temple.GUS.MMA.Neighborhoods.*;
import edu.temple.GUS.MMA.util.StatisticalOperator;
import edu.temple.GUS.MMA.Exceptions.MMAFunctionInitException;

/**
 *
 * @author  Jeff Leong
 */
public class LocalEqualTo {
    
    /** Creates a new instance of LocalEqualTo */
    public LocalEqualTo() {}
    
    public MMATimeSeries execute(MMATimeSeries tSeries, float value) throws MMAFunctionInitException
    {
        if (tSeries == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series is not initialized.");
        
        // Initialze the t dimension of the timeseries and the noData value
        int t = tSeries.getT();
        float noData = tSeries.getNoData();

        // Output variables
        MMATimeSeries outputTSeries = new MMATimeSeries(t);
        float resultData[] = new float[t];
        
        for ( int i=0; i <t; i++)
            if (tSeries.getElement(i) == noData)
                resultData[i] = noData;
            else
                resultData[i] = (tSeries.getElement(i) > value) ? 1.0f : 0.0f;
        
        outputTSeries.putData(resultData);
        outputTSeries.putNoData(noData);
        
        return outputTSeries;
    }
    
    public MMATimeSeries execute(MMATimeSeries tSeries1, MMATimeSeries tSeries2) throws MMAFunctionInitException
    {
        if ((tSeries1 == null) || (tSeries2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series are not initialized.");

        // Initialze the t dimension of timeseries 1 and the noData value
        int t = tSeries1.getT();
        float noData = tSeries1.getNoData();

        // Output variables
        MMATimeSeries outputTSeries = new MMATimeSeries(t);
        float resultData[] = new float[t];
                
        // Ensure the time series have the identical dimensions
        if (t != tSeries2.getT())
            throw new MMAFunctionInitException(this.getClass().toString() + " Boundaries mismatch.");
            
        for ( int i=0; i<t; i++)
            if ((tSeries1.getElement(i) == noData) || (tSeries2.getElement(i) == noData))
            {
                System.out.println("tSeries1.getElement(i)::" + tSeries1.getElement(i) + "tSeries2.getElement(i)::" + tSeries2.getElement(i));
                resultData[i] = noData;
            }    
            else
            {
                System.out.println("tSeries1.getElement(i)::" + tSeries1.getElement(i) + "tSeries2.getElement(i)::" + tSeries2.getElement(i));
                resultData[i] = (tSeries1.getElement(i) == tSeries2.getElement(i)) ? 1.0f : 0.0f;
            }    
        outputTSeries.putData(resultData);
        outputTSeries.putNoData(noData);
        
        return outputTSeries;
    }
    
    public MMATimeSeries execute(MMATimeSeries tSeries1, MMATimeSeries tSeries2, TLag lag) throws MMAFunctionInitException
    {
        if ((tSeries1 == null) || (tSeries2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series are not initialized.");

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");

        // Initialze the t dimension of timeseries 1 and the noData value
        int t = tSeries1.getT();
        int tOffset;
        float noData = tSeries1.getNoData();

        // Output variables
        MMATimeSeries outputTSeries = new MMATimeSeries(t);
        float resultData[] = new float[t];
        
        tOffset = lag.getT();

        // Ensure the time series have the identical dimensions
        if (t != tSeries2.getT())
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
            
        for ( int i=0; i <t; i++)
            // Check if the index is within the boundary
            if (((tOffset + i) < 0 ) || ((tOffset + i) >= t ))
                resultData[i] = noData;
            else
                if ((tSeries1.getElement(i) == noData) || (tSeries2.getElement(tOffset + i) == noData))
                    resultData[i] = noData;
                else
                    resultData[i] = (tSeries1.getElement(i) == tSeries2.getElement(tOffset + i)) ? 1.0f : 0.0f;
        
        outputTSeries.putData(resultData);
        outputTSeries.putNoData(noData);
        
        return outputTSeries;
    }
    
    /* MMA Grid Functions */
    public MMAGrid execute(MMAGrid grid, float value) throws MMAFunctionInitException
    {       
        if ( grid == null )
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");
            
        // Initialize x, y, data, noData
        int x = grid.getX();
        int y = grid.getY();
        float noData = grid.getNoData();

        // Output variables
        MMAGrid outputGrid = new MMAGrid(x,y);
        float resultData[][] = new float[y][x];
        
        for ( int j=0; j <y; j++)
            for ( int i=0; i <x; i++)
                // Check for no data value
                if ((grid.getElement(i, j) == noData) || (value == noData))
                    resultData[j][i] = noData;
                else
                    resultData[j][i] = (grid.getElement(i, j) == value) ? 1.0f : 0.0f;
        
        outputGrid.putData(resultData);
        outputGrid.putNoData(noData);
        
        return outputGrid;
    }
    
    public MMAGrid execute(MMAGrid grid1, MMAGrid grid2)  throws MMAFunctionInitException
    {
        if (( grid1 == null ) || ( grid2 == null ))
            throw new MMAFunctionInitException(this.getClass().toString() + " Grids are not initialized.");

        // Initialze the dimensions of grid 1 and the noData value
        int x = grid1.getX();
        int y = grid1.getY();
        float noData = grid1.getNoData();

        // Output variables
        MMAGrid outputGrid = new MMAGrid(x,y);
        float resultData[][] = new float[y][x];
        float temp;
        
        // Ensure the grids have the identical dimensions
        if ((x != grid2.getX()) || (y != grid2.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
            
        for ( int j=0; j <y; j++)
            for ( int i=0; i <x; i++)
                if ((grid1.getElement(i, j) == noData) || (grid2.getElement(i, j) == noData))
                    resultData[j][i] = noData;
                else
                    resultData[j][i] = (grid1.getElement(i, j) == grid2.getElement(i, j)) ? 1.0f : 0.0f;
        
        outputGrid.putData(resultData);
        outputGrid.putNoData(noData);
        
        return outputGrid;
    }
    
    public MMAGrid execute( MMAGrid grid1, MMAGrid grid2, XYLag lag) throws MMAFunctionInitException
    {
        if (( grid1 == null ) || ( grid2 == null ))
            throw new MMAFunctionInitException(this.getClass().toString() + " Grids are not initialized.");

        if ( lag == null )
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");

        // Initialze the dimensions of grid 1 and the noData value
        int x = grid1.getX();
        int y = grid1.getY();
        int xOffset, yOffset;
        float noData = grid1.getNoData();

        // Output variables
        MMAGrid outputGrid = new MMAGrid(x,y);
        float resultData[][] = new float[grid1.getY()][grid1.getX()];
        float temp;
        
        // Ensure the grids have the identical dimensions
        if ((x != grid2.getX()) || (y != grid2.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
            
        xOffset = lag.getX();
        yOffset = lag.getY();
        
        for ( int j=0; j <y; j++)
            for ( int i=0; i <x; i++)
                // Check if the indexes are within the boundaries
                if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ))
                    resultData[j][i] = noData;
                else
                    if ((grid1.getElement(i,j) == noData) || (grid2.getElement(xOffset+i, yOffset+j) == noData))
                        resultData[j][i] = noData;
                    else
                        resultData[j][i] = (grid1.getElement(i, j) == grid2.getElement(xOffset+i, yOffset+j)) ? 1.0f : 0.0f;
        
        outputGrid.putData(resultData);
        outputGrid.putNoData(noData);
        
        return outputGrid;
    }
    
    /* MMA Time Cube Functions */
    public MMATimeCube execute( MMATimeCube tCube, float value) throws MMAFunctionInitException
    {
        if ( tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    
        
        // Initialze the dimensions of timecube and the noData value
        int x = tCube.getX();
        int y = tCube.getY();
        int t = tCube.getT();
        float noData = tCube.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                    if (tCube.getElement(i,j,k) == noData)
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = (tCube.getElement(i,j,k) == value) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }
    
    public MMATimeCube execute( MMATimeCube tCube, MMATimeSeries timeSeries) throws MMAFunctionInitException
    {
        if ( tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    
        
        if ( timeSeries == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series is not initialized.");    

        // Initialze the dimensions of timecube and the noData value
        int x = tCube.getX();
        int y = tCube.getY();
        int t = tCube.getT();
        float noData = tCube.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        // Ensure the t dimension match with those of the time cube
        if (t != timeSeries.getT())
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                    if ((tCube.getElement(i,j,k) == noData) || (timeSeries.getElement(k) == noData))
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = (tCube.getElement(i,j,k) == timeSeries.getElement(k)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMATimeCube execute( MMATimeCube tCube, MMAGrid grid) throws MMAFunctionInitException
    {
        if ( tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    
        
        if ( grid == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");    

        // Initialze the dimensions of timecube and the noData value
        int x = tCube.getX();
        int y = tCube.getY();
        int t = tCube.getT();
        float noData = tCube.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        // Ensure the x and y dimensions match with those of the time cube
        if ((x != grid.getX()) || (y != grid.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                    if ((tCube.getElement(i,j,k) == noData) || (grid.getElement(i,j) == noData))
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = (tCube.getElement(i,j,k) == grid.getElement(i,j)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }
    
    public MMATimeCube execute( MMATimeCube tCube1, MMATimeCube tCube2) throws MMAFunctionInitException
    {
        if (( tCube1 == null) || ( tCube2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cubes are not initialized.");    

        // Initialze the dimensions of timecube 1 and the noData value
        int x = tCube1.getX();
        int y = tCube1.getY();
        int t = tCube1.getT();
        float noData = tCube1.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        // Ensure the time cubes have the identical dimensions
        if ((x != tCube2.getX()) || (y != tCube2.getY()) || (t != tCube2.getT()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                    // Check for noData value
                    if ((tCube1.getElement(i,j,k) == noData) || (tCube2.getElement(i,j,k) == noData))
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = (tCube1.getElement(i,j,k) == tCube2.getElement(i,j,k)) ? 1.0f : 0.0f;
                                
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMATimeCube execute( MMATimeCube tCube, MMATimeSeries timeSeries, TLag lag) throws MMAFunctionInitException
    {
        if ( tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    
        
        if ( timeSeries == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series is not initialized.");    

        if ( lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of timecube and the noData value
        int x = tCube.getX();
        int y = tCube.getY();
        int t = tCube.getT();
        int tOffset;
        float noData = tCube.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        // Ensure the t dimension match with those of the time cube
        if (t != timeSeries.getT())
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        tOffset = lag.getT();

        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                {
                    // Check if the indexes are within the boundaries
                    if (((tOffset + k) < 0 ) || ((tOffset + k) >= t ))
                        resultData[k][j][i] = noData;
                    else
                        if ((tCube.getElement(i,j,k) == noData) || (timeSeries.getElement(tOffset + k) == noData))
                            resultData[k][j][i] = noData;
                        else
                            resultData[k][j][i] = (tCube.getElement(i,j,k) == timeSeries.getElement(tOffset + k)) ? 1.0f : 0.0f;
                }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }

    public MMATimeCube execute( MMATimeCube tCube, MMAGrid grid, XYLag lag) throws MMAFunctionInitException
    {
        if ( tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    
        
        if ( grid == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");    

        if ( lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of timecube and the noData value
        int x = tCube.getX();
        int y = tCube.getY();
        int t = tCube.getT();
        int xOffset, yOffset;
        float noData = tCube.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        xOffset = lag.getX();
        yOffset = lag.getY();
        
        // Ensure the x and y dimensions match with those of the time cube
        if ((x != grid.getX()) || (y != grid.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                {
                    // Check if the indexes are within the boundaries
                    if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ))
                        resultData[k][j][i] = noData;
                    else
                        if ((tCube.getElement(i,j,k) == noData) || (grid.getElement(i,j) == noData))
                            resultData[k][j][i] = noData;
                        else
                            resultData[k][j][i] = (tCube.getElement(i,j,k) == grid.getElement(xOffset + i, yOffset + j)) ? 1.0f : 0.0f;
                }
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }

    public MMATimeCube execute( MMATimeCube tCube1, MMATimeCube tCube2, XYTLag lag) throws MMAFunctionInitException
    {
        if (( tCube1 == null) || ( tCube2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cubes are not initialized.");    
        
        if ( lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of timecube 1 and the noData value
        int x = tCube1.getX();
        int y = tCube1.getY();
        int t = tCube1.getT();
        int xOffset, yOffset, tOffset;
        float noData = tCube1.getNoData();

        // Output variables
        MMATimeCube outputCube = new MMATimeCube(x,y,t);
        float resultData[][][] = new float[t][y][x];
        float temp;
        
        xOffset = lag.getX();
        yOffset = lag.getY();
        tOffset = lag.getT();

        // Ensure the time cubes have the identical dimensions
        if ((x != tCube2.getX()) || (y != tCube2.getY()) || (t != tCube2.getT()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<t; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                    // Check if the indexes are within the boundaries
                    if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((tOffset + k) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((tOffset + k) >= t ))
                        resultData[k][j][i] = noData;
                    else
                        // Check for noData value
                        if ((tCube1.getElement(i,j,k) == noData) || (tCube2.getElement(i,j,k) == noData))
                            resultData[k][j][i] = noData;
                        else
                            resultData[k][j][i] = (tCube1.getElement(i,j,k) == tCube2.getElement(xOffset + i, yOffset + j, tOffset + k)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }

    // Spacecube functions

    public MMASpaceCube execute( MMASpaceCube sCube, float value) throws MMAFunctionInitException
    {
        if (sCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube is not initialized.");

        // Initialze the dimensions of the spacecube and the noData value
        int x = sCube.getX();
        int y = sCube.getY();
        int z = sCube.getZ();
        float noData = sCube.getNoData();   
         
        // Output variables
        MMASpaceCube outputCube = new MMASpaceCube(x,y,z);
        float resultData[][][] = new float[z][y][x];
        float temp;

        for ( int k=0; k<z; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                    if (sCube.getElement(i,j,k) == noData)
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = (sCube.getElement(i,j,k) == value) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMASpaceCube execute( MMASpaceCube sCube, MMAGrid grid) throws MMAFunctionInitException
    {
        if ( sCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube is not initialized.");    
        
        if ( grid == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");    

        // Initialze the dimensions of the spacecube and the noData value
        int x = sCube.getX();
        int y = sCube.getY();
        int z = sCube.getZ();
        float noData = sCube.getNoData();   
         
        // Output variables
        MMASpaceCube outputCube = new MMASpaceCube(x,y,z);
        float resultData[][][] = new float[z][y][x];
        float temp;

        // Ensure the x and y dimensions match with those of the space cube
        if ((x != grid.getX()) || (y != grid.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<z; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                    if ((sCube.getElement(i,j,k) == noData) || (grid.getElement(i,j) == noData))
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = (sCube.getElement(i,j,k) == grid.getElement(i,j)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMASpaceCube execute( MMASpaceCube sCube1, MMASpaceCube sCube2) throws MMAFunctionInitException
    {
        if ((sCube1 == null) || (sCube2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cubes are not initialized.");    
        
        // Initialze the dimensions of the spacecube1 and the noData value
        int x = sCube1.getX();
        int y = sCube1.getY();
        int z = sCube1.getZ();
        float noData = sCube1.getNoData();   

        // Output variables
        MMASpaceCube outputCube = new MMASpaceCube(x,y,z);
        float resultData[][][] = new float[z][y][x];
        float temp;
        
        // Ensure the space cubes have the identical dimensions
        if ((x != sCube2.getX()) || (y != sCube2.getY()) || (z != sCube2.getZ()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
            
        for ( int k=0; k<z; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                    // Check for noData value
                    if ((sCube1.getElement(i,j,k) == noData) || (sCube2.getElement(i,j,k) == noData))
                        resultData[k][j][i] = noData;
                    else
                        resultData[k][j][i] = (sCube1.getElement(i,j,k) == sCube2.getElement(i,j,k)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMASpaceCube execute( MMASpaceCube sCube, MMAGrid grid, XYLag lag)  throws MMAFunctionInitException
    {
        if (sCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube list is not initialized.");    

        if (grid == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of spacecube and the noData value
        int x = sCube.getX();
        int y = sCube.getY();
        int z = sCube.getZ();
        int xOffset, yOffset;
        float noData = sCube.getNoData();

        // Output variables
        MMASpaceCube outputCube = new MMASpaceCube(x,y,z);
        float resultData[][][] = new float[z][y][x];
        float temp;
        
        xOffset = lag.getX();
        yOffset = lag.getY();
        
        // Ensure the x and y dimensions match with those of the space cube
        if ((x != grid.getX()) || (y != grid.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<z; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                    // Check if the indexes are within the boundaries
                    if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ))
                        resultData[k][j][i] = noData;
                    else
                        if ((sCube.getElement(i,j,k) == noData) || (grid.getElement(i,j) == noData))
                            resultData[k][j][i] = noData;
                        else
                            resultData[k][j][i] = (sCube.getElement(i,j,k) == grid.getElement(xOffset + i, yOffset + j)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }
    
    public MMASpaceCube execute( MMASpaceCube sCube1, MMASpaceCube sCube2, XYZLag lag) throws MMAFunctionInitException
    {
        if ((sCube1 == null) || (sCube2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cubes are not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of the spacecube and the noData value
        int x = sCube1.getX();
        int y = sCube1.getY();
        int z = sCube1.getZ();
        int xOffset, yOffset, zOffset;
        float noData = sCube1.getNoData();   

        // Output variables
        MMASpaceCube outputCube = new MMASpaceCube(x,y,z);
        float resultData[][][] = new float[z][y][x];
        float temp;
 
        // Ensure the space cubes have the identical dimensions
        if ((x != sCube2.getX()) || (y != sCube2.getY()) || (z != sCube2.getZ()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;
            
        xOffset = lag.getX();
        yOffset = lag.getY();
        zOffset = lag.getZ();
        
        for ( int k=0; k<z; k++)
            for ( int j=0; j<y; j++)
                for ( int i=0; i<x; i++)
                    // Check if the indexes are within the boundaries
                    if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((zOffset + k) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((zOffset + k)>=z ))
                        resultData[k][j][i] = noData;
                    else
                        // Check for noData value
                        if ((sCube1.getElement(i,j,k) == noData) || (sCube2.getElement(i,j,k) == noData))
                            resultData[k][j][i] = noData;
                        else
                            resultData[k][j][i] = (sCube1.getElement(i,j,k) == sCube2.getElement(i,j,k)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    // Hypercube functions
    public MMAHyperCube execute( MMAHyperCube hCube, float value) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                        if (hCube.getElement(i,j,k,l) == noData)
                            resultData[l][k][j][i] = noData;
                        else
                            resultData[l][k][j][i] = (hCube.getElement(i,j,k,l) == value) ? 1.0f : 0.0f;

        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
        
    public MMAHyperCube execute( MMAHyperCube hCube, MMATimeSeries timeSeries) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (timeSeries == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the x and y dimensions match with those of the hypercube
        if (t != timeSeries.getT())
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                        if ((hCube.getElement(i,j,k,l) == noData) || (timeSeries.getElement(l) == noData))
                            resultData[l][k][j][i] = noData;
                        else
                            resultData[l][k][j][i] = (hCube.getElement(i,j,k,l) == timeSeries.getElement(l)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMAGrid grid) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (grid == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the x and y dimensions match with those of the hypercube
        if ((x != grid.getX()) || (y != grid.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                        if ((hCube.getElement(i,j,k,l) == noData) || (grid.getElement(i,j) == noData))
                            resultData[l][k][j][i] = noData;
                        else
                            resultData[l][k][j][i] = (hCube.getElement(i,j,k,l) == grid.getElement(i,j)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMATimeCube tCube) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        float noData = hCube.getNoData(); 
        
        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the dimension of input time cube match that of the hypercube
        if ((x != tCube.getX()) || (y != tCube.getY()) || (t != tCube.getT()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int k=0; k<z; k++)
            for ( int l=0; l<t; l++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                        // Check for noData value
                        if ((hCube.getElement(i,j,k,l) == noData) || (tCube.getElement(i,j,l) == noData))
                            resultData[l][k][j][i] = noData;
                        else
                            resultData[l][k][j][i] = (hCube.getElement(i,j,k,l) == tCube.getElement(i,j,l)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMASpaceCube sCube) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (sCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the dimension of input space cube match that of the hypercube
        if ((x != sCube.getX()) || (y != sCube.getY()) || (z != sCube.getZ()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                        // Check for noData value
                        if ((hCube.getElement(i,j,k,l) == noData) || (sCube.getElement(i,j,k) == noData))
                            resultData[l][k][j][i] = noData;
                        else
                            resultData[l][k][j][i] = (hCube.getElement(i,j,k,l) == sCube.getElement(i,j,k)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube1, MMAHyperCube hCube2) throws MMAFunctionInitException
    {
        if ((hCube1 == null) || (hCube2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cubes are not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube1.getX();
        int y = hCube1.getY();
        int z = hCube1.getZ();
        int t = hCube1.getZ();
        float noData = hCube1.getNoData(); 
        
        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;

        // Ensure the hyper cubes have the identical dimensions
        if ((x != hCube2.getX()) || (y != hCube2.getY()) || (z != hCube2.getZ()) || (t != hCube2.getT()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                        // Check for noData value
                        if ((hCube1.getElement(i,j,k,l) == noData) || (hCube2.getElement(i,j,k,l) == noData))
                            resultData[l][k][j][i] = noData;
                        else
                            resultData[l][k][j][i] = (hCube1.getElement(i,j,k,l) == hCube2.getElement(i,j,k,l)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMATimeSeries timeSeries, TLag lag) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (timeSeries == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time series is not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        int tOffset;
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the x and y dimensions match with those of the hypercube
        if (t != timeSeries.getT())
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        tOffset = lag.getT();

        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                        // Check if the indexes are within the boundaries
                        if (((tOffset + l) < 0 ) || ((tOffset + l) >= t ))
                            resultData[l][k][j][i] = noData;
                        else
                            if ((hCube.getElement(i,j,k,l) == noData) || (timeSeries.getElement(tOffset + l) == noData))
                                resultData[l][k][j][i] = noData;
                            else
                                resultData[l][k][j][i] = (hCube.getElement(i,j,k,l) == timeSeries.getElement(tOffset + l)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMAGrid grid, XYLag lag) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (grid == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Grid is not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        int xOffset, yOffset;
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the x and y dimensions match with those of the hypercube
        if ((x != grid.getX()) || (y != grid.getY()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        xOffset = lag.getX();
        yOffset = lag.getY();

        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                        // Check if the indexes are within the boundaries
                        if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ))
                            resultData[l][k][j][i] = noData;
                        else
                            if ((hCube.getElement(i,j,k,l) == noData) || (grid.getElement(i,j) == noData))
                                resultData[l][k][j][i] = noData;
                            else
                                resultData[l][k][j][i] = (hCube.getElement(i,j,k,l) == grid.getElement(xOffset + i,yOffset + j)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMATimeCube tCube, XYTLag lag) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (tCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Time cube is not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        int xOffset, yOffset, tOffset;
        float noData = hCube.getNoData(); 
        
        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the dimension of input time cube match that of the hypercube
        if ((x != tCube.getX()) || (y != tCube.getY()) || (t != tCube.getT()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        xOffset = lag.getX();
        yOffset = lag.getY();
        tOffset = lag.getT();

        for ( int k=0; k<z; k++)
            for ( int l=0; l<t; l++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                        // Check if the indexes are within the boundaries
                        if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((tOffset + l) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((tOffset + l) >= t ))
                            resultData[l][k][j][i] = noData;
                        else
                            // Check for noData value
                            if ((hCube.getElement(i,j,k,l) == noData) || (tCube.getElement(i,j,l) == noData))
                                resultData[l][k][j][i] = noData;
                            else
                                resultData[l][k][j][i] = (hCube.getElement(i,j,k,l) == tCube.getElement(xOffset + i,yOffset + j,tOffset + l)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube, MMASpaceCube sCube, XYZLag lag) throws MMAFunctionInitException
    {
        if (hCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cube is not initialized.");    

        if (sCube == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Space cube is not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube.getX();
        int y = hCube.getY();
        int z = hCube.getZ();
        int t = hCube.getT();
        int xOffset, yOffset, zOffset;
        float noData = hCube.getNoData(); 

        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;
        
        // Ensure the dimension of input space cube match that of the hypercube
        if ((x != sCube.getX()) || (y != sCube.getY()) || (z != sCube.getZ()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        xOffset = lag.getX();
        yOffset = lag.getY();
        zOffset = lag.getZ();
        
        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                        // Check if the indexes are within the boundaries
                         if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((zOffset + k) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((zOffset + k)>=z ))
                            resultData[l][k][j][i] = noData;
                        else
                            // Check for noData value
                            if ((hCube.getElement(i,j,k,l) == noData) || (sCube.getElement(i,j,k) == noData))
                                resultData[l][k][j][i] = noData;
                            else
                                resultData[l][k][j][i] = (hCube.getElement(i,j,k,l) == sCube.getElement(xOffset + i, yOffset + j, zOffset + k)) ? 1.0f : 0.0f;
        
        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;        
    }
    
    public MMAHyperCube execute( MMAHyperCube hCube1, MMAHyperCube hCube2, XYZTLag lag) throws MMAFunctionInitException
    {
        if ((hCube1 == null) || (hCube2 == null))
            throw new MMAFunctionInitException(this.getClass().toString() + " Hyper cubes are not initialized.");    

        if (lag == null)
            throw new MMAFunctionInitException(this.getClass().toString() + " Lag is not initialized.");    

        // Initialze the dimensions of the hypercube and the noData value
        int x = hCube1.getX();
        int y = hCube1.getY();
        int z = hCube1.getZ();
        int t = hCube1.getZ();
        int xOffset, yOffset, zOffset, tOffset;
        float noData = hCube1.getNoData(); 
        
        // Output variables
        MMAHyperCube outputCube = new MMAHyperCube(x,y,z,t);
        float resultData[][][][] = new float[t][z][y][x];
        float temp;

        // Ensure the hyper cubes have the identical dimensions
        if ((x != hCube2.getX()) || (y != hCube2.getY()) || (z != hCube2.getZ()) || (t != hCube2.getT()))
            throw new MMAFunctionInitException(this.getClass().toString() +  " Boundaries mismatch."); ;

        xOffset = lag.getX();
        yOffset = lag.getY();
        zOffset = lag.getZ();
        tOffset = lag.getT();
        
        for ( int l=0; l<t; l++)
            for ( int k=0; k<z; k++)
                for ( int j=0; j<y; j++)
                    for ( int i=0; i<x; i++)
                        // Check if the indexes are within the boundaries
                         if (((xOffset + i) < 0 ) || ((yOffset + j) < 0 ) || ((zOffset + k) < 0 ) || ((tOffset + l) < 0 ) || ((xOffset + i) >= x ) || ((yOffset + j) >= y ) || ((zOffset + k)>=z )|| ((tOffset + l)>=t))
                            resultData[l][k][j][i] = noData;
                        else
                            // Check for noData value
                            if ((hCube1.getElement(i,j,k,l) == noData) || (hCube2.getElement(i,j,k,l) == noData))
                                resultData[l][k][j][i] = noData;
                            else
                                resultData[l][k][j][i] = (hCube1.getElement(i,j,k,l) == hCube2.getElement(xOffset + i,yOffset + j,zOffset + k,tOffset + l)) ? 1.0f : 0.0f;

        outputCube.putData(resultData);
        outputCube.putNoData(noData);
        
        return outputCube;
    }
    
}
